import mergeOptions from "./utils/merge-options";
import { newGUID } from "./utils/guid";
import stateful from "./mixins/stateful";
import { domainToUnicode } from "url";


/**
 * 所有的ui组件的基类
 * component就是一个代表着js对象和DOM元素的UI对象
 * 他们可以是其他组件的子组件， 也可以自己拥有组件
 *
 * components也可以使用来自EventTarget的方法
 */
class Component {

  /**
   *
   * @param {*} player 应该挂载的player对象
   * @param {*} options key/value 形式的player options
   * @param {*} [options.children] 一个数组
   * @param {Component~ReadyCallback} [ready] component ready之后的回调
   */
  constructor (player, options, ready) {

    // player也可能是当前对象
    if (!player, options, ready) {
      this.player_ = player = this
    } else {
      this.player_ = player
    }

    this.options_ = mergeOptions({}, this.options_)

    options = this.options_ = mergeOptions(this.options_, options)

    // 获取id
    this.id_ = options.id || (options.el && options.el.id)

    // 如果没有id，就生成一个
    if (!this.id_) {
      const id = player && player.id && player.id() || 'no_player'
      this.id_ = `${id}_component_${Guid.newGUID()}`
    }

    this.name_ = options.name || null

    if (options.el) {
      this.el_ = options.el
    } else if (options.createEl !== false) {
      this.el_ = createEl()
    }

    // evented ?? 这个事什么东西
    if (options.evented !== false) {
      evented(this, {eventBusKey: this.el_ ? 'el_' : null})
    }
    // 这个又是什么东西
    stateful(this, this.constructor.defaultState)


    this.children_ = []
    this.childIndex_  = {}
    this.childNameIndex_ = {}
    // 添加options.initChildren(func)中的子组件
    if (options.initChildren !== false) {
      this.initChildren()
    }

    // 触发ready函数
    this.ready(ready)

    // 是否需要报告touch动作
    if (options.reportTouchActivity !== false) {
      this.enableTouchActivity()
    }
  }

  /**
   * 清除component和所有的子components
   */
  dispose () {

    // 发出不冒泡的清除事件
    this.trigger({type: 'dispose', bubbles: false})

    // 清除子组件
    if (this.children_) {
      for (let i = this.children_.length - 1; i >= 0; i--) {
        if (this.children_[i].dispose) {
          this.children_[i].dispose()
        }
      }
    }

    // 删除dom和，domdata中存储的数据
    if (this.el_) {
      if (this.el_.parentNode) {
        this.el_.parentNode.removeChild(this.el_)
      }

      DomData.removeData(this.el_)
      this.el_ = null
    }
    this.player_ = null
  }

  /**
   * 获取当前组件挂载的播放器
   */
  player () {
    return this.player_
  }

  /**
   * 设置或者获取options
   * @param {object} obj
   */
  options (obj) {
    console.log('废弃方法： 使用constructor')
    if (!obj) {
      return this.options_
    }
    this.options_ = mergeOptions(this.options_, obj)
    return this.options_
  }

  el () {
    return this.el_
  }

  /**
   * 创建dom元素
   * @param {*} tagName 标签
   * @param {*} properties 值
   * @param {*} attributes 属性
   */
  createEl (tagName, properties, attributes) {
    return Dom.createEl(tagName, properties, attributes)
  }

  /**
   * 给一个英文字符，返回本地化后的字符
   * @param {*} string
   * @param {*} tokens
   * @param {*} defaultValue
   */
  localize (string, tokens, defaultValue = string) {
    const code = this.player_.language && this.player_.language()
    const languages = this.player_.languages && this.player_.languages()
    const language = languages && languages[code]
    const primaryCode = code && code.split('-')[0]
    const primaryLang = languages && languages[primaryCode]

    let localizedString = defaultValue
    if (language && language[string]) {
      localizedString = language[string]
    } else if (primaryLang && primaryLang[string]) {
      localizedString  = primaryLang[string]
    }

    if (tokens) {
      localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
        const value = tokens[index - 1]
        let ret = value
        if (typeof value === 'undefined') {
          ret = match
        }
        return ret
      })
    }

    return localizedString
  }

  /**
   * 返回dom element, 这就是children插入的地方,通常和el是同一个
   */
  contentEl () {
    return this.contentEl_ || this.el_
  }

  id() {
    return this.id_;
  }

  name() {
    return this.name_;
  }

  children() {
    return this.children_;
  }

  /**
   * 通过id获取子component
   * @param {} id
   */
  getChildById(id) {
    return this.childIndex_[id];
  }

  /**
   * 通过name获取子component
   * @param {*} name
   */
  getChild(name) {
    if (!name) {
      return;
    }

    name = toTitleCase(name);

    return this.childNameIndex_[name];
  }

  /**
   * 往当前组件中添加一个组件
   * @param {*} child
   * @param {*} options
   * @param {*} index
   */
  addChild (child, options = {}, index = this.children_.length) {
    let component
    let componentName

    // 初始化或者直接使用这个child
    if (typeof child === 'string') {
      componentName = toTitleCAse(child)
      const componentClassName = options.componentClass || componentName
      options.name = componentName

      // 获取一个组件
      const ComponentClass = Component.getComponent(componentClassName)

      if (!ComponentClass) {
        throw new Error(`Component ${componentClassName}不存在`)
      }

      // 兼容4.x 确定component class 可以被init, vjs 中直接存储的数据可能会被错误识别成component
      if (typeof ComponentClass !== 'function') {
        return null
      }

      // 初始化
      component = new ComponentClass(this.player_ || this, options)
    } else {
      component = child
    }

    // 强势插入
    this.children_.splice(index, 0, component)

    if (typeof component.id === 'function'){
      // 记录
      this.childIndex_[component.id()] = component
    }

    componentName = componentName || (component.name && toTitleCase(component.name()))
    if (componentName) {
      // 记录
      this.childNameIndex_[componentName] = component
    }

    // 如果存在el，就创建并添加
    if (typeof component.el === 'function' && component.el()) {
      const childNodes = this.contentEl().children
      const refNode = childNodes[index] || null

      this.contentEl().insertBefore(component.el(), refNode)
    }
    return component
  }

  removeChild (component) {
    if (typeof component === 'string') {
      component = this.getChild(component)
    }
    if (!component || !this.children_) {
      return
    }

    // 删除记录
    let childFound = false
    for (let i = this.children_.length - 1; i>= 0; i--) {
      if (this.children_[i] === component) {
        childFound = true
        this.children_.splice(i, 1)
        break
      }
    }
    if (!childFound) {
      return
    }

    // 删除记录 为什么addChild时需要判断component.id，删除时不用判断??
    this.childIndex_[component.id()] = null
    this.childNameIndex_[component.name()] = null

    // 删除dom
    const compEl = component.el()
    if (compEl && compEl.parentNode === this.contentEl()) {
      this.contentEl().removeChild(component.el())
    }
  }

  initChildren () {
    const children = this.options_.children
    if (children) {
      const parentOptions = this.options_
      const handleAdd = child => {
        const name = child.name
        let opts = child.opts

        if (parentOptions[name] !== undefined) {
          opts = parentOptions[name]
        }
        if (opts === false) {
          return
        }
        if (opts === true) {
          opts = {}
        }
        opts.playerOptions = this.options_.playerOptions
        const newChild = this.addChild(name, opts)
        if (newChild) {
          this[name] = newChild
        }
      }

      let workingChildren
      const Tech = Component.getComponent('Tech')
      if (Array.isArray(children)) {
        workingChildren = children
      } else {
        workingChildren = Object.keys(children) // names
      }

      workingChildren
        .concat(Object.keys(this.options_).filter(child => {
          // 去除workingChildren里面有的
          return !workingChildren.some(wchild => {
            if (typeof wchild === 'string') {
              return child === wchild
            }
            return child === wchild.name
          })
        }))

        // 矫正name, opts
        .filter(child => {
          let name
          let opts
          if (typeof child === 'string') {
            name = child
            opts = children[name] || this.options_[name] || {}
          } else {
            name = child.name
            opts = child
          }
        })
        // 去除tech
        .filter(child => {
          const c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name))
          return c && !Tech.isTech(c)
        })
        // 逐个添加
        .forEach(handleAdd)

    }
  }

  // 创建css classname, 应该被子类重写
  buildCSSClass() {
    // Child classes can include a function that does:
    // return 'CLASS NAME' + this._super();
    return '';
  }

  ready (fn, sync = false) {
    if (!fn) {
      return
    }

    if (!this.isReady_) {   // 还没初始化好
      this.readyQueue_ = this.readyQueue_ || []
      this.readyQueue_.push(fn)
      return
    }

    // 初始化好了
    if (sync) {
      fn.call(this)
    } else {
      this.setTimeout(fn, 1)
    }
  }

  // 触发所有的ready listeners
  triggerReady () {
    this.isReady_ = true

    this.setTimeout(function () {
      const readyQueue = this.readyQueue_

      // 重置readyQueue_
      this.readyQueue_ = []
      if (readyQueue && readyQueue.length > 0) {
        readyQueue.forEach(function (fn) {
          fn.call(this)
        }, this)
      }

      // ?? 触发EventTarget的ready事件
      this.trigger('ready')
    }, 1)
  }

  // 暴露$方法 querySelector
  $ (selector, context) {
    return Dom.$(selector, context || this.contentEl())
  }

  // 暴露$$方法 querySelectorAll
  $$ (selector, context) {
    return Dom.$$(selector, context || this.contentEl())
  }

  hasClass(classToCheck) {
    return Dom.hasClass(this.el_, classToCheck);
  }

  addClass(classToAdd) {
    Dom.addClass(this.el_, classToAdd);
  }

  removeClass(classToRemove) {
    Dom.removeClass(this.el_, classToRemove);
  }

  toggleClass(classToToggle, predicate) {
    Dom.toggleClass(this.el_, classToToggle, predicate);
  }

  show() {
    this.removeClass('vjs-hidden');
  }

  hide() {
    this.addClass('vjs-hidden');
  }

  // 在fadeIn/fadeOut时使用，锁定组件的visible state
  lockShowing() {
    this.addClass('vjs-lock-showing');
  }

  unlockShowing() {
    this.removeClass('vjs-lock-showing');
  }
  getAttribute(attribute) {
    return Dom.getAttribute(this.el_, attribute);
  }
  setAttribute(attribute, value) {
    Dom.setAttribute(this.el_, attribute, value);
  }
  removeAttribute(attribute) {
    Dom.removeAttribute(this.el_, attribute);
  }

  /**
   * 通过css样式获取或者设置宽度
   * @param {*} num css长度
   * @param {*} skipListeners 跳过resize事件触发
   */
  width(num, skipListeners) {
    return this.dimension('width', num, skipListeners);
  }
  height(num, skipListeners) {
    return this.dimension('height', num, skipListeners);
  }
  dimensions(width, height) {
    // Skip componentresize listeners on width for optimization
    this.width(width, true);
    this.height(height);
  }

  /**
   * 获取或者设置宽度或者高度, style.width -> window.getComputedStyle
   * @param {*} widthOrHeight 'width' or 'height'
   * @param {*} num
   * @param {*} skipListeners
   */
  dimension (widthOrHeight, num, skipListeners) {
    if (num !== undefined) {
      if (num === null || num !== num) {
        num = 0
      }

      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
        this.el_.style[widthOrHeight] = num
      } else if (num === 'auto') {
        this.el_.style[widthOrHeight] = ''
      } else {
        this.el_.style[widthOrHeight] = num + 'px'
      }

      if (!skipListeners) {
        this.trigger('componentresize')
      }
      return
    }
    if (!this.el_) {
      return 0
    }

    const val = this.el_.style[widthOrHeight]
    const pxIndex = val.indexOf('px')
    if (pxIndex !== -1) {
      return parseInt(val.slice(0, pxIndex), 10)
    }
    return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10)
  }

  /**
   * 通过window.getComputedStyle获取宽度或高度
   * @param {*} widthOrHeight
   */
  currentDimension (widthOrHeight) {
    let computedWidthOrHeight = 0
    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
      throw new Error('只支持"width"、"height"')
    }
    if (typeof window.getComputedStyle === 'function') {
      const computedStyle = window.getComputedStyle(this.el_)
      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight]
    }
    computedWidthOrHeight = parseFloat(computedWidthOrHeight)
    if (computedWidthOrHeight === 0) {
      const rul = `offset${toTittleCase(widthOrHeight)}`
      computedWidthOrHeight = this.el_[rule]
    }
    return computedWidthOrHeight
  }

  setTimeout (fn, timeout) {
    fn = Fn.bind(this, fn)

    const timeoutId = window.setTimeout(fn, timeout)
    const disposeFn = () => this.clearTimeout(timeoutId)

    disposeFn.guid = `vjs-timeout-${timeoutId}`

    // 卸载时清除事件
    this.on('dispose', disposeFn)
    return timeoutId
  }

  currentDimensions() {
    return {
      width: this.currentDimension('width'),
      height: this.currentDimension('height')
    };
  }

  currentWidth() {
    return this.currentDimension('width');
  }

  currentHeight() {
    return this.currentDimension('height');
  }

  focus() {
    this.el_.focus();
  }

  blur() {
    this.el_.blur();
  }

  clearTimeout (timeoutId) {
    window.clearTimeout(timeoutId)

    const disposeFn = function () {}

    this.off('dispose', disposeFn)

    return timeoutId
  }

  /**
   * 如果tap事件支持的话触发tap事件 ??
   */
  emitTapEvents () {
    let touchStart = 0
    let firstTouch = null
    let couldBeTap

    const tapMovementThreshold = 10
    const touchTimeThreshold = 200

    this.on('touchstart', function (event) {
      if (event.touches.length === 1) {
        firstTouch = {
          pageX: event.touches[0].pageX,
          pageY: event.touches[0].pageY
        }
        touchStart = new Date().getTime()
        couldBeTap = true
      }
    })
    this.on('touchmove', function (event) {
      if (event.touches.length > 1) {
        couldBeTap = false
      } else if (firstTouch) {
        const xdiff = event.touches[0].pageX - firstTouch.pageX
        const ydiff = event.touches[0].pageY - firstTouch.pageY
        const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff)

        if (touchDistance > tapMovementThreshold) {
          couldBeTap = flse
        }
      }
    })

    const noTap = function () {
      couldBeTap = false
    }
    this.on('touchleave', noTap)
    this.on('touchcancel', noTap)
    this.on('touchend', function (event) {
      firstTouch = null
      if (couldBeTap === true) {
        const touchTime = new Date().getTime() - touchStart
        if (touchTime < touchTimeThreshold) {
          event.preventDefault()
          this.trigger('tap')
        }
      }
    })
  }

  /**
   *
   */
  enableTouchActivity () {
    if (!this.player() || !this.player().reportUserActivity) {
      return
    }

    const report = Fn.bind(this.player(), this.player().reportTouchActivity)

    let touchHolding
    this.on('touchstart', function () {
      report()
      this.clearInterval(touchHolding)
      touchHolding = this.setInterval(report, 250)
    })
    const touchEnd = function (event) {
      report()
      this.clearInterval(touchHolding)
    }
    this.on('touchmove', report)
    this.on('touchend', touchEnd)
    this.on('touchcancel', touchEnd)
  }

  /**
   * 注册组件
   * @param {*} name
   * @param {*} ComponentToRegister
   */
  static registerComponent (name, ComponentToRegister) {
    if (typeof name !== 'string' || !name) {
      throw new Error('name必须是飞空字符串')
    }

    const Tech = Component.getComponent('Tech')
    const isComp = Component === ComponentToRegister ||
      Component.prototype.isPrototypeOf(ComponentToRegister.prototype)
    if (isTech || !isComp) {
      let reason
      if (isTech) {
        reason = 'techs应该使用Tech.registerTech()方法注册'
      } else {
        reason = '必须继承Component'
      }
      throw new Error(`非法的component, "${name}"; ${reason}.`)
    }

    name = toTitleCase(name)

    if (!Component.components_) {
      Component.components_ = {}
    }
    const Player = Component.getComponent('Player')

    // 如果注册的是Player
    if (name === 'Player' && Player && Player.players) {
      const players = Player.players
      const playerNames = Object.keys(players)

      if (
        players &&
        playerNames.length > 0 &&
        playerNames.map(pname => players[pname]).every(Boolean)
      ) {
        throw new Error('player创建之后不能再注册Player组件了')
      }
    }

    Component.components_[name] = ComponentToRegister
    return ComponentToRegister
  }

  static getComponent (name) {
    if (!name) return

    name = toTitleCase(name)

    if (Component.components_ && Component.components_[name]) {
      return Component.components_[name]
    }
  }

}




/**
 * 是否支持requestAnimationFrame 用于测试
 *
 */
Component.prototype.supportsRaf_ = typeof window.requestAnimationFrame === 'function' &&
  typeof window.cancelAnimationFrame === 'function'
Component.registerComponent('Component', Component)
export default Component
